package map.adapter;


import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import map.tools.interactions.LinksTable;
import map.tools.interactions.SelectionCriteria;
import map.tools.uimodel.ChoiceSerializer;
import map.tools.uimodel.UIModelSeriralizer;

import org.eclipse.emf.ecore.util.EcoreUtil;

import uimigration.Interactions.Behaviour;
import uimigration.Plateform.Plateform;
import uimigration.UIModel.Container;
import uimigration.UIModel.UIElement;
import uimigration.UIModel.UIModel;
import uimigration.UIModel.UIModelFactory;
import uimigration.WidgetsModel.Widget;
import uimigration.widgetscorrepondances.ChoiceModel;
import uimigration.widgetscorrepondances.Equivalents;
import uimigration.widgetscorrepondances.FUIElement;
import uimigration.widgetscorrepondances.WidgetscorrepondancesFactory;

public class UIAdaptator {

	public UIModel sourceUIModel;
	public HashMap<Integer,UIModel> targetUIModelMap=null;
	private static int idBase=0;
	private ChoiceModel memoryChoice;
	public int UIModelNum=0; //Incremented after uimodel was cloned
	public Plateform sourcePlateform, targetPlateform;
	public LinksTable sourceLinks, targetLinks;
	public SelectionCriteria criteria;
	public WidgetsFinder finder;
	public WidgetsSelector selector;
	public UIElementFactory factory;
	private boolean DEBUG=false;
	public Vector<UIElement> market=null;
	private HashMap<Integer, List<Integer>> mapId=null;
	private String choiceFile=null;
	private HashMap<String, List<Widget>> foundWidget=null;
	private MappingTableManager table;
	
	public UIAdaptator(String apiS, String apiT) {
		finder= new WidgetsFinder(apiT,apiS);
		selector= new WidgetsSelector();	
		targetUIModelMap =new HashMap<Integer, UIModel>();
		idBase=0;
		market= new Vector<UIElement>();
		mapId= new HashMap<Integer, List<Integer>>();
		setTable();
	}
	public UIAdaptator(String apiS,String apiT, String fileName ) {

		finder= new WidgetsFinder(apiT,apiS);
		selector= new WidgetsSelector();	
		targetUIModelMap =new HashMap<Integer, UIModel>();
		idBase=0;
		market= new Vector<UIElement>();
		mapId= new HashMap<Integer, List<Integer>>();
		memoryChoice= WidgetscorrepondancesFactory.eINSTANCE.createChoiceModel();
		choiceFile=fileName;
		setTable();
	}
	public void processStep(){
		//Create UIModel & set values
		UIModel targetUIModel= UIModelFactory.eINSTANCE.createUIModel();
		idBase=0;
		targetUIModel.setName(sourceUIModel.getName()+"_Target");
		targetUIModelMap.put(0, targetUIModel);
		BFSUInodeChoice((Container) sourceUIModel.getContains().get(0));		
		new  ChoiceSerializer().save(memoryChoice, choiceFile);
	}
	public void processUIModel(){
		//Create UIModel & set values
		UIModel targetUIModel= UIModelFactory.eINSTANCE.createUIModel();
		idBase=0;
		targetUIModel.setName(sourceUIModel.getName()+"_Target");
		targetUIModelMap.put(0, targetUIModel);
		BFSUInode((Container) sourceUIModel.getContains().get(0));
	}
	private void BFSUInodeChoice(Container root)
	{
		LinkedList<UIElement> queue= new LinkedList<UIElement>();
		market.add(root);
		queue.addLast(root);
		while(!queue.isEmpty()){
			UIElement current= queue.removeFirst();
			int dadId=findDadId(root, current.getId());
			if(current!=null)
			{
				try
				{
					if(DEBUG)
						System.out.println("Current= "+current.getWidgetType()+" queue size"+queue.size());
					migrateNodeChoice(current,dadId);	
					Container c=(Container)current;
					if(c.getContainsUIElement()!=null&&c.getContainsUIElement().size() >0){
						for(int i=0;i <c.getContainsUIElement().size();i++){
							UIElement sibling=c.getContainsUIElement().get(i);
							if(sibling!=null&&!marked(sibling)){
								market.add(sibling);
								queue.addLast(sibling);
							}
						}
					}	
				}catch (ClassCastException e){
				}
			}
		}
		
	}

	private void migrateNodeChoice(UIElement current, int dadId) {
		boolean newF=false;
		List<uimigration.WidgetsModel.Widget> listW= null;
		if(foundWidget==null)
			foundWidget= new HashMap<String, List<Widget>>();
		if(foundWidget.get(current.getWidgetType().getName()) == null )
		{
			listW = finder.find2(current);
			
			newF= true;
			foundWidget.put(current.getWidgetType().getName(), listW);
		}
		else
		{	
			if(finder.hasMoreImplementedBehavior(current, current.getWidgetType()))
			{	
				listW = finder.find2(current);
				newF= true;
			}
			else
				listW= foundWidget.get(current.getWidgetType().getName());
			
		}
		UIElement retElement=null;
		if(listW!=null && listW.size()>0){
			
			retElement=UIElementFactory.instantiateElement(current,listW.get(0),idBase,table);
			setMapId(current, retElement);
			setTargetUIMapModelChoice(retElement,current,dadId);
			if(newF)
			{
				setChoice(retElement,listW);
				newF=false;
			}
			idBase++;
		}
		
	}
	
	private void setChoice( UIElement uielement,List<Widget> lw){
		if(lw!=null && lw.size()>0){
			FUIElement el= WidgetscorrepondancesFactory.eINSTANCE.createFUIElement();
			el.setName(uielement.getName());
			el.setWidgetType(lw.get(0).getName());
			UIElement copy=EcoreUtil.copy( uielement);
			try
			{
				if(((Container)copy).getContainsUIElement()!=null)
					((Container)copy).getContainsUIElement().clear();
			}catch(Exception e)
			{
				
			}
			el.setUielement(copy);
			for(int i=1;i<lw.size();i++){
				Equivalents eq= WidgetscorrepondancesFactory.eINSTANCE.createEquivalents();
				eq.setName(uielement.getName());
				eq.setWidgetType(lw.get(i).getName());
				el.getWidgets().add(eq);
				int id= uielement.getId();
				eq.setUielement(UIElementFactory.instantiateElement(uielement, lw.get(i), id, table));
			}
			
			memoryChoice.getUielements().add(el);
		}
	}

	private void BFSUInode(Container root)
	{
		LinkedList<UIElement> queue= new LinkedList<UIElement>();
		market.add(root);
		queue.addLast(root);
		while(!queue.isEmpty()){
			UIElement current= queue.removeFirst();
			int dadId=findDadId(root, current.getId());
			if(current!=null)
			{
				try
				{
					if(DEBUG)
						System.out.println("Current= "+current.getName()+" queue size"+queue.size());
					migrateNode(current,dadId);	
					Container c=(Container)current;
					if(c.getContainsUIElement()!=null&&c.getContainsUIElement().size() >0){
						for(int i=0;i <c.getContainsUIElement().size();i++){
							UIElement sibling=c.getContainsUIElement().get(i);
							if(sibling!=null&&!marked(sibling)){
								market.add(sibling);
								queue.addLast(sibling);
							}
						}
					}	
				}catch (ClassCastException e){
				}
			}
		}
		
	}

	private void migrateNode(UIElement current,int dadId) {
		List<uimigration.WidgetsModel.Widget> listW=finder.find(current);
		int mapSize=targetUIModelMap.size();
		UIElement retElement=null;
		//UIElement newElement=null;
		if(listW!=null && listW.size()>0){
			int childNb = listW.size();
			if(DEBUG)
				System.out.println("migrateNode "+listW.toString()+" mapSize "+mapSize+" childNb="+childNb);
			copyTargetUIModelMap(childNb,mapSize);
			for(int index=0;index<childNb;index++)
			{	
				Widget newWidget=listW.get(index);
				retElement=UIElementFactory.instantiateElement(current, newWidget,idBase,table);
				setMapId(current, retElement);
				setTargetUIMapModel(mapSize, retElement,current,dadId, index);
			}
			idBase++;
		}
	}

	private void setMapId(UIElement current, UIElement retElement) {
		if(mapId==null)
			mapId= new HashMap<Integer, List<Integer>>();
		if(mapId.get(current.getId())==null){
			List<Integer> li= new ArrayList<Integer>();
			li.add(retElement.getId());
			mapId.put(current.getId(), li );
		}
		else
			mapId.get(current.getId()).add(retElement.getId());
		if(DEBUG)
			System.out.println("setMapId "+mapId.toString());
	}

	private int getMapId(Container container, int id){
		if(mapId.get(id)!=null&&mapId.get(id).size()>0)
		{
			List<Integer> li=mapId.get(id);
			Vector<Integer> vi=new Vector<Integer>();
			vi.addAll(li);
			if(li.size()>1)
				for(int i:li){
					int c =findDadId(container, i);
					if(c!=-1 && !vi.contains(c))
					{
						if(container.findUIElement(c)!=null || container.getId()==c)
							return i;
					}
				}
			else 
				return li.get(0);
		}
		if(DEBUG)
		{
			System.out.println("getMapId id="+id+" Container="+container.toString());
			System.out.println("getMapId targetUIModelMap="+targetUIModelToString());
		}
		return -1;
	}
	private int findDadId(Container current, int id ) {
		int idi=-1;
		for(UIElement e: current.getContainsUIElement())
			try {
				if(e.getId()==id)
					return current.getId();
				else
					idi= findDadId((Container) e, id);
				if(idi!=-1)
					return idi;
			} catch (ClassCastException e1) {
			}
		return idi;
	}
	private boolean marked(UIElement current) {		
		return market.contains(current);
	}
	private void copyTargetUIModelMap(int nbChild,int mapSize){
		if(nbChild>1)
		for(int i=1;i<nbChild;i++)
				for(int j=0;j<mapSize;j++)
					if(targetUIModelMap.get(mapSize*i+j )==null)
						targetUIModelMap.put(mapSize*i+j, EcoreUtil.copy( targetUIModelMap.get(j)));
		
	}
	private void setTargetUIMapModel(int mapSize, UIElement retElement, UIElement oldElement, int oldDadId,
			int index) {
		try {
			for(int i=0;i<mapSize;i++)
			{ 
                int ni=index*mapSize+i;
  				if(targetUIModelMap.get(ni)!=null )
                {
  					int dadId=-1;
  					if(targetUIModelMap.get(ni).getContains().size()>0)
  						dadId=getMapId((Container) targetUIModelMap.get(ni).getContains().get(0), oldDadId);
  					if(DEBUG)
  						System.out.println("setTargetUIMapModel "+mapSize+"  ret="+retElement.getName()+" ret.id="+retElement.getId()+" index="+index +" dadId "+dadId+" ni="+ni+" oldDadId="+oldDadId );
                    if(dadId<0)
                    {        
                    	if(targetUIModelMap.get(ni).getContains().size()<=0)
                    		targetUIModelMap.get(ni ).getContains().add(retElement);
                    }
                    else if( targetUIModelMap.get(ni).findUIElement(dadId)!=null)
                    {   
                     	if(((Container) targetUIModelMap.get(ni).findUIElement(dadId)).getWidgetType()!=null&&((Container) targetUIModelMap.get(ni).findUIElement(dadId)).getWidgetType().getContainsWidgetLength() >0)
                    	{
                    		Container cont=UIElementFactory.instantiateElementSame(oldElement,((Container) targetUIModelMap.get(ni).findUIElement(dadId)),((Container) targetUIModelMap.get(ni).findUIElement(dadId)).getWidgetType().getContainsWidget().get(0),table);
                    		setMapId(oldElement, cont);
                    		cont.getContainsUIElement().add((UIElement)EcoreUtil.copy(retElement));
                    		((Container) targetUIModelMap.get(ni).findUIElement(dadId)).getContainsUIElement().add((UIElement)EcoreUtil.copy(cont));

                    	}
                    	else
                    	{
                    		((Container) targetUIModelMap.get(ni).findUIElement(dadId)).getContainsUIElement().add((UIElement)EcoreUtil.copy(retElement));
                    	}
                    }
                    else if(DEBUG)
                    {   	
                    	System.out.println("Should never happen!!");
                    	System.out.println("dadId="+dadId+" not found ni="+ni+" retElement "+retElement.getName());
                    }
                }
  				else if(DEBUG)
                {    	
                	System.out.println("Should never happen!! ni="+ni+" \n"+targetUIModelToString());
                 }
  					
 			}
			} catch (Exception e) {
			e.printStackTrace();
		}
	}
	private void setTargetUIMapModelChoice(  UIElement newElement, UIElement oldElement, int oldDadId) {
		try {
			int ni=0;
			if(targetUIModelMap.get(ni)!=null )
                {
  					int dadId=-1;
  					if(targetUIModelMap.get(ni).getContains().size()>0)
  						dadId=getMapId((Container) targetUIModelMap.get(ni).getContains().get(0), oldDadId);
                    if(dadId<0)
                    {        
                    	if(targetUIModelMap.get(ni).getContains().size()<=0)
                    		targetUIModelMap.get(ni ).getContains().add(newElement);
                    }
                    else if( targetUIModelMap.get(ni).findUIElement(dadId)!=null)
                    	((Container) targetUIModelMap.get(ni).findUIElement(dadId)).getContainsUIElement().add((UIElement)EcoreUtil.copy(newElement));
                }
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public String testFind(UIElement odle,UIElement newe, List<Widget> found)
	{
		String ret="\nOldUIElement";
		if(odle!=null)
		{	
			ret+=odle.toString();
			List<Behaviour> lb=odle.getWidgetType().getBehavior();
			for(Behaviour b :lb )
				ret+=b.toString();
		}
		ret+="\nNewUIElement";
		if(newe!=null)
		{	
			ret+=newe.toString();
			List<Behaviour> lb=newe.getWidgetType().getBehavior();
			for(Behaviour b :lb )
				ret+=b.toString();
		}

 		ret+="\nFound :";
		for(Widget w:found){
			ret+="Name :"+w.getName()+"\n";
			for(Behaviour bi:w.getBehavior())
				ret+=bi.getName()+"\n";
		}
		return ret;
	}
	public String targetUIModelToString()
	{
		String st="\n targetUIModelMap.Size="+targetUIModelMap.size();
		for(UIModel u:targetUIModelMap.values()){
			st+="\n"+u.toString()+"\nContains ";
			for(UIElement e: u.getContains()){
				st+="\n"+e.toString();
			}
		}
		return st;
	}
	public UIModel myCopier(UIModel model)
	{
		if(model!=null){
			UIModel re= null;
			UIModelSeriralizer ser= new UIModelSeriralizer();
			ser.save(model,"./temmod.xmi" );
			File f=new File("./temmod.xmi");
			re = (UIModel) ser.load(f);
			f.delete();
			return re;
		}
		else return null;
	}
	private void setTable() {
		List<String> target =new ArrayList<String>();
		target.add("models/device/DeviceContact.xmi");
		List<String> source= new ArrayList<String>();
		source.add("models/device/DeviceMouse.xmi");
		source.add("models/device/DeviceKeyBoard.xmi");
		table = new MappingTableManager("models/tables/MappingTable.xmi", target, source);
	}

}

